const fs = require('fs');
const drawer = require('./drawer');
const SKIP = "skip_result_when_possible";

/**
 * 拼装数据结构绘制处理器
 */
let globalChainer = (function () {
    let unionFindDrawer = new drawer.UnionFindDrawer();
    let dinicAlogDrawer = new drawer.DinicAlogDrawer();
    let arrayDrawer = new drawer.ArrayDrawer();
    let voidDrawer = new drawer.VoidDrawer();
    let DumpDrawer = new drawer.DumpDrawer();

    unionFindDrawer.setNextProcessor(dinicAlogDrawer)
        .setNextProcessor(arrayDrawer)
        .setNextProcessor(DumpDrawer)
        .setNextProcessor(voidDrawer);

    return unionFindDrawer;
})();

/**
 * 测试结果是否等于验证结果
 * 
 * @param {*} raw 测试结果
 * @param {*} check 验证结果
 * @returns true 相等
 */
function doCompare(raw, check) {
    if(check == SKIP) {
        return true;
    }

    if (raw instanceof Array) {
        if (!(check instanceof Array) || raw.length != check.length) {
            return false;
        }

        for (let i = 0; i < raw.length; ++i) {
            if (raw[i] != check[i]) {
                return false;
            }
        }

        return true;
    }

    return raw === check;
}

/**
 * 比较结果
 * 
 * @param {*} result 结果
 * @param {*} checks 检查列表
 * @param {*} index 索引
 * @returns 比较结果
 */
function compare(result, checks, index) {
    return (checks == null || checks.length == 0 || checks[index] == SKIP) ? SKIP : doCompare(result, checks[index]);
}

/**
 * 求平均值
 * 
 * @param {*} arr 数值数组 
 * @returns 
 */
function average(arr) {
    return arr.reduce((a, b) => a + b, 0) / arr.length;
}

/**
 * 求方差
 * 
 * @param {*} arr 数值数组 
 * @returns 求方差
 */
function standard(arr) {
    let avg = average(arr);
    return Math.sqrt(arr.map(x => (x - avg) ** 2).reduce((a, b) => a + b, 0) / arr.length);
}

/**
 * 快速验证工具
 * 
 * @param {*} cases 案例集合
 * @param {*} checks 验证集合
 * @param {*} range 筛选范围
 * @returns 
 */
exports.testing = function (cases = [], checks = [], range = [0, cases.length]) {
    if(checks != SKIP && cases.length != checks.length) {
        console.log(`unmatch cases length:${cases.length} and checks length:${checks.length}`);        
    }

    return (callback = (x) => x) => {
        let results = [], times = [], corrects = 0;
        cases = cases.slice(range[0], range[1]? range[1]: cases.length);
        checks = checks == SKIP ? SKIP: checks.slice(range[0], range[1]? range[1]: checks.length);
        for (let i = 0; i < cases.length; ++i) {
            let start = Date.now(),
                result = callback(cases[i]),
                head = `case ${(i + range[0] + 1)}:`;
            times.push(Date.now() - start);
            let correct = compare(result, checks, i);
            corrects += correct ? 1 : 0;
            let cr = (correct == SKIP || checks == SKIP)? "":`<${correct}>`;
            results.push([head, result, cr].join(" "));
        }

        console.log(results.join("\n"));
        console.log(`runtime: ${times.reduce((a, b) => a + b, 0)} ms`);
        console.log(`avgtime: ${(average(times)).toFixed(2)}±${standard(times).toFixed(2)} ms`);
        if (checks != null) console.log(`correct: ${corrects}/${cases.length} ${(corrects / cases.length * 100).toFixed(2)}%`);
        console.log("-------------------------------------------------------------------------");
    }
};

/**
 * 输出为markdown绘图文本
 * 
 * @param {*} dataStruct 某个数据结构 
 * @param {*} title 标题
 * @param {*} append 是否以追加方式写入
 * @param {*} prefix 前缀
 * @param {*} surfix 后缀
 */
exports.drawing = function (dataStruct, title = "default", append = true, prefix = "", surfix = "") {
    if (!fs.existsSync('./dist')) {
        fs.mkdirSync('./dist');
    }

    let fileName = `./dist/${title}.md`;
    fs.writeFile(fileName, `${prefix}\n${globalChainer.process(dataStruct)}\n${surfix}`, {
        encoding: 'utf-8',
        flag: append ? 'a' : 'w'
    }, () => {
        console.log(`file ${fileName} --> fileSystem done.`);
    });
}

exports.SKIP = SKIP;
